;nasm -f elf -o kernel.o kernel.asm  (or nasm -f elf32 -o kernel.o kernel.asm)
;ld -s -Ttext 0x30400 -o KERNEL.BIN kernel.o (or ld -s -m elf_i386 -Ttext 0x30400 -o KERNEL.BIN kernel.o)
;注意必须是在Linux系统编译、链接
;
; ----------------------------------------------------------------------
; 编译连接方法:
; $ rm -f kernel.bin
; $ nasm -f elf32 -o kernel.o kernel.asm
; $ nasm -f elf32 -o string.o string.asm
; $ gcc -c -m32 -o KeStart.o KeStart.c
; $ ld -s -m elf_i386  -Ttext 0x30400 -o kernel.bin kernel.o string.o KeStart.o  
; $ rm -f kernel.o string.o KeStart.o
; $ 
; ----------------------------------------------------------------------
SELECTOR_KERNEL_CS	equ	8
SELECTOR_KERNEL_DS	equ	10h
INT_M_CTL			equ	0x20	; I/O port for interrupt controller         <Master>
INT_M_CTLMASK		equ	0x21	; setting bits in this port disables ints   <Master>
INT_S_CTL			equ	0xA0	; I/O port for second interrupt controller  <Slave>
INT_S_CTLMASK		equ	0xA1	; setting bits in this port disables ints   <Slave>

EOI					equ	0x20
;进程表结构
P_STACKBASE			equ	0
GSREG				equ	P_STACKBASE
FSREG				equ	GSREG		+ 4
ESREG				equ	FSREG		+ 4
DSREG				equ	ESREG		+ 4
EDIREG				equ	DSREG		+ 4
ESIREG				equ	EDIREG		+ 4
EBPREG				equ	ESIREG		+ 4
KERNELESPREG		equ	EBPREG		+ 4
EBXREG				equ	KERNELESPREG	+ 4
EDXREG				equ	EBXREG		+ 4
ECXREG				equ	EDXREG		+ 4
EAXREG				equ	ECXREG		+ 4
RETADR				equ	EAXREG		+ 4
EIPREG				equ	RETADR		+ 4
CSREG				equ	EIPREG		+ 4
EFLAGSREG			equ	CSREG		+ 4
ESPREG				equ	EFLAGSREG	+ 4
SSREG				equ	ESPREG		+ 4
P_STACKTOP			equ	SSREG		+ 4
P_LDT_SEL			equ	P_STACKTOP
P_LDT				equ	P_LDT_SEL	+ 4

TSS_S_ESP0			equ	4

SELECTOR_TSS		equ		0x20
; 导入函数
extern	KeStart
extern	exception_handler
extern	interupt_hanler
extern  schdule
extern 	clock_handler

; 导入全局变量
extern	gdt_ptr
extern	idt_ptr
extern  tss
extern  irq_table
extern  sys_call_table
extern  p_proc_ready
extern  k_reenter

[section .bss]
StackSpace		resb	2 * 1024
StackTop:		; 栈顶

[section .text]
global _start;
global restart
global sys_call
;中断handler
global	divide_error
global	single_step_exception
global	nmi
global	breakpoint_exception
global	overflow
global	bounds_check
global	inval_opcode
global	copr_not_available
global	double_fault
global	copr_seg_overrun
global	inval_tss
global	segment_not_present
global	stack_exception
global	general_protection
global	page_fault
global	copr_error
global  hwint00
global  hwint01
global  hwint02
global  hwint03
global  hwint04
global  hwint05
global  hwint06
global  hwint07
global  hwint08
global  hwint09
global  hwint10
global  hwint11
global  hwint12
global  hwint13
global  hwint14
global  hwint15
_start:
; 把 esp 从 LOADER 挪到 KERNEL
	mov		esp, StackTop	; 堆栈在 bss 段中

	sgdt	[gdt_ptr]		; KeStart() 中将会用到 gdt_ptr
	call	KeStart		; 在此函数中改变了gdt_ptr，让它指向新的GDT
	lgdt	[gdt_ptr]		; 使用新的GDT

	lidt	[idt_ptr]
	
	jmp		SELECTOR_KERNEL_CS:csinit
csinit:						; “这个跳转指令强制使用刚刚初始化的结构”——<<OS:D&I 2nd>> P90.
	
	xor		eax, eax
	mov		ax, SELECTOR_TSS
	ltr		ax

	push	0
	popfd	; Pop top of stack into EFLAGS
	
	jmp     schdule
	sti
	
	jmp $

save:
;	sub	esp, 4		;当call save时 ESP+4 保存了返回地址
	pushad		; `.
	push	ds	;  |
	push	es	;  | 保存原寄存器值
	push	fs	;  |
	push	gs	; /
	
	mov     dx, ss ;is SELECTOR_KERNEL_DS
	mov     ds, dx
	mov     es, dx
	
	mov 	esi, esp		;esi = 进程表起始地址
	;mov 	esp, StackTop
	
	inc	dword [k_reenter]
	cmp	dword [k_reenter], 0
	;jne	.re_enter ;改用此方法可以在重入和不重入都响应中断
	
	jne 	.1
	mov 	esp, StackTop	;内核堆栈
	push	restart			;ret  时执行
	jmp     .2
.1:							;已经在内核堆栈
	push 	re_enter		;ret   时执行
.2:
	jmp     [esi + RETADR - P_STACKBASE] ;相当于ret
restart:
	
	mov		esp, [p_proc_ready]		   	  ;进程表基址
	lldt	[esp + P_LDT_SEL] 			  ;局部描述符选择子
	lea		eax, [esp + P_STACKTOP]		  ;本次中断返回后执行进程表内指向的进程
	mov		dword [tss + TSS_S_ESP0], eax ;下一次中断发生时esp指向进程表，保存进程数据
	
re_enter:	; 如果(k_reenter != 0)，会跳转到这里

	dec		dword [k_reenter]
	pop		gs
	pop		fs
	pop		es
	pop		ds
	popad

	add		esp, 4

	iretd		;返回后执行进程体代码
%include "interupt.inc"
